package com.beiding.service.impl;

import com.beiding.dao.ResourceOperations;
import com.beiding.dao.RoomDao;
import com.beiding.dao.UserDao;
import com.beiding.exception.BadRequestException;
import com.beiding.exception.NotFoundException;
import com.beiding.pojo.Room;
import com.beiding.pojo.User;
import com.beiding.service.ResourceService;
import com.beiding.service.RoomStorageService;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.security.Principal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

@Service
public class RoomStorageServiceImpl implements RoomStorageService, ResourceService<Room> {

    private RoomDao dao;

    private UserDao userDao;

    @Autowired
    public void setDao(RoomDao dao) {
        this.dao = dao;
    }

    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    @Cacheable(value = "room",key = "'room_insensitive_'+#id")
    public Room getRoom(String id) {
        Optional<Room> optional = dao.findByIdLimitFields(id, Lists.newArrayList(
                "id", "name", "location", "prices", "images", "description"
        ));
        return optional.orElse(null);
    }

    @Override
    @Cacheable(value = "room",key = "'room_'+#id")
    public Room getRoomDetails(String id) {

        return get(id);
    }

    @Override
    @CachePut(value = "room",key = "'room_'+#room.id")
    public Room save(Room room) {
        room.setName(uniqName(room.getHolderId(), room.getName()));
        dao.save(room);
        return room;
    }

    @Override
    @Caching(evict = {
            @CacheEvict(value = "room", key = "'room_'+#room.id"),
            @CacheEvict(value = "room", key = "'room_insensitive_'+#room.id")
    })
    public Room update(Principal principal, Room room) {

        checkHolder(room.getId(), principal.getName());

        //找到本地房源
        Room tRoom = getRoom(room.getId());

        if (tRoom.getTheUser() != null) {
            throw new BadRequestException("房间正在使用中无法修改");
        }

        //如果名称发生改变
        if (!tRoom.getName().equals(room.getName())) {
            room.setName(uniqName(room.getHolderId(), room.getName()));
        }

        tRoom.merge(room);

        //将允许修改的信息merge到本地房源

        dao.save(tRoom);
        return tRoom;
    }

    @Override
    @Caching(evict = {
            @CacheEvict(value = "room", key = "'room_'+#id"),
            @CacheEvict(value = "room", key = "'room_insensitive_'+#id"),
            @CacheEvict(value = "room",key = "'state_'+#id")

    })
    public void remove(Principal principal, String id) {
        checkHolder(id, principal.getName());

        String theUser = dao.findByIdLimitFields(id, "theUser").get().getTheUser();

        if (theUser != null) {
            throw new BadRequestException("房间正在使用中无法删除");
        }

        dao.deleteById(id);
    }


    //获取附近房源
    @Override
    public List<Room> nearby(double elng, double wlng, double nlat, double slat, Pageable pageable) {
        Assert.isTrue(pageable.getPageSize() < 10, "每次最多获取9个房源");
        Assert.isTrue(pageable.getPageNumber() == 0, "页数只能为'0'");
        return dao.nearbyRooms(elng, wlng, nlat, slat, pageable);
    }

    @Override

    @Caching(evict = {
            @CacheEvict(value = "room", key = "'room_'+#roomId"),
            @CacheEvict(value = "room", key = "'room_insensitive_'+#roomId"),
            @CacheEvict(value = "room",key = "'state_'+#roomId")

    })
    public void publish(Principal principal, String roomId, Boolean publishState) {
        checkHolder(roomId, principal.getName());
        dao.updateField(roomId, "published", publishState);
    }

    @Override
    public Boolean isFavorite(String name, String id) {
        return userDao.containerContains(name, "favoriteRooms", id);
    }

    @Override
    public void favorite(String name, String id, Boolean state) {

        if (state) {
            userDao.containerAppend(name, "favoriteRooms", id);
        } else {
            userDao.containerRemove(name, "favoriteRooms", id);
        }
    }

    @Override
    public List<Room> getFavoriteRooms(String name) {

        Optional<User> optional = userDao.findByIdLimitFields(name, "favoriteRooms");

        if (optional.isPresent()) {
            User user = optional.get();
            List<String> favoriteRooms = user.getFavoriteRooms();

            List<Room> r = new ArrayList<>();

            favoriteRooms.forEach(s -> {
                Room room = getRoom(s);
                if (room != null) {
                    r.add(room);
                }
            });

            return r;
        } else {
            return Collections.EMPTY_LIST;
        }
    }

    @Override

    @Caching(evict = {
            @CacheEvict(value = "room", key = "'room_'+#id"),
            @CacheEvict(value = "room", key = "'room_insensitive_'+#id"),
            @CacheEvict(value = "room",key = "'state_'+#id")

    })
    public void occupy(String id, String theUser) {


        Optional<Room> optional = dao.findByIdLimitFields(id, "theUser");

        if (optional.isPresent()) {

            Room room = optional.get();

            if (room.getTheUser() != null) {
                throw new BadRequestException("房源正在被使用中不能重复占有");
            }

            dao.updateField(id, "theUser", theUser);
            return;
        }

        throw new NotFoundException("未找到指定房源");

        //将房间变化为占用状态
    }

    @Override
    @Caching(evict = {
            @CacheEvict(value = "room", key = "'room_'+#id"),
            @CacheEvict(value = "room", key = "'room_insensitive_'+#id"),
            @CacheEvict(value = "room",key = "'state_'+#id")

    })
    public void release(String id) {
        if (dao.existsById(id)) {
            dao.updateField(id, "theUser", null);
        }
    }

    @Override
    @Cacheable(value = "room",key = "'state_'+#roomId")
    public String getState(String roomId) {

        Optional<Room> room = dao.findByIdLimitFields(roomId, "published", "theUser");

        if (room.isPresent()) {

            Room r = room.get();

            if (!r.isPublished()) {
                return "unpublished";
            } else if (r.getTheUser() == null) {
                return "published";
            } else {
                return "using";
            }

        }

        throw new NotFoundException("房源未找到");

    }

    @Override
    public ResourceOperations<Room> resourceOperations() {
        return dao;
    }


    private String uniqName(String holderId, String name) {
        if (dao.existsByHolderIdAndName(holderId, name)) {
            int i = name.lastIndexOf('_');
            if (i == -1) {
                name = name + "_1";
            } else {
                int n = Integer.valueOf(name.substring(i + 1));
                name = name.substring(0, i + 1) + (1 + n);
            }
            return uniqName(holderId, name);
        }

        return name;
    }

}
